package com.abo.yaas.webservice.utils;

import java.io.Serializable;

import com.abo.yaas.domain.User;
import com.abo.yaas.webservice.CriteriaQueryUtil;
import com.abo.yaas.webservice.exceptions.InvalidCredentialsException;
import com.abo.yaas.webservice.exceptions.WebServiceException;

/**
 * Controller for managing user accounts
 */
@SuppressWarnings("serial")
public class UserFacade implements Serializable {
    public static User changePassword(User user) throws WebServiceException {
        verifyPasswordLength(user.getPassword());
        checkPasswordVerification(user.getPassword(), user.getDuplicatePassword());
        user.setPasswordHash(PasswordUtil.generateHashedPassword(user.getPassword()));
        return user;
    }

    /**
     * Verifies that the given passwords match with each other
     * 
     * @param password
     *            Password to check
     * @param verifyPassword
     *            Verification of the first password
     * @throws PasswordsDoNotMatchException
     *             Thrown if the given parameters do not equal eachother
     */
    private static void checkPasswordVerification(String password, String verifyPassword) throws WebServiceException {
        if (!password.equals(verifyPassword)) {
            throw new WebServiceException("Passwords do not match!");
        }
    }

    /**
     * Checks if the given username is available.
     * 
     * @param login
     *            Desired login
     * @return Returns true if the login doesn't exist, false if it exists
     */
    private static boolean checkUsernameAvailability(String login) {
        return getUserByLogin(login) == null;
    }

    /**
     * Creates and stores a user with the given username and password
     * 
     * @param username
     *            The username for the user
     * @param password
     *            The user's password
     * @return The created {@link User} object
     */
    public static void encryptPassword(final User user) {
        user.setPasswordHash(PasswordUtil.generateHashedPassword(user.getPassword()));
    }

    /**
     * Returns the minimum length of a password
     * 
     * @return Minimum password length
     */
    public static int getMinPasswordLength() {
        return PasswordUtil.getMinPasswordLength();
    }

    /**
     * Returns the minimum length of a username
     * 
     * @return Minimum username length
     */
    public static int getMinUsernameLength() {
        final String minLenghtStr = System.getProperty("authentication.username.validation.length");
        int minLenght = 4;
        if (minLenghtStr == null) {
            System.setProperty("authentication.username.validation.length", "4");
            return minLenght;
        }

        try {
            minLenght = Integer.valueOf(minLenghtStr);
        } catch (final NumberFormatException e) {
            throw new IllegalArgumentException("authentication.username.validation.length must be an integer");
        }

        return minLenght;
    }

    public static User getUserByLogin(String login) {
        return CriteriaQueryUtil.getEntityByProperty("login", login, User.class);
    }

    /**
     * Change the password of the given user. Verifies that the new password
     * meets all the set conditions.
     * 
     * @param user
     *            User object for which we want to change the password
     * @param currentPassword
     *            Current password
     * @param newPassword
     *            Desired new password
     * @param verifiedNewPassword
     *            New password verification
     * @throws InvalidCredentialsException
     *             Thrown if the current password is incorrect
     * @throws TooShortPasswordException
     *             Thrown if the new password is too short
     * @throws PasswordsDoNotMatchException
     *             Thrown if the password verification fails
     */
    public static void validatePasswordChange(User user, String currentPassword, String newPassword,
            String verifiedNewPassword) throws InvalidCredentialsException, WebServiceException {
        // Verify that the current password is correct
        if (!PasswordUtil.verifyPassword(user, currentPassword)) {
            throw new InvalidCredentialsException();
        }

        // Check the new password's constraints
        verifyPasswordLength(newPassword);
        checkPasswordVerification(newPassword, verifiedNewPassword);

        user.setPasswordHash(PasswordUtil.generateHashedPassword(newPassword));
    }

    /**
     * Makes sure the given password is long enough
     * 
     * @param password
     *            Password to check
     * @throws TooShortPasswordException
     *             Thrown if password is null or too short
     */
    private static void verifyPasswordLength(String password) throws WebServiceException {
        if (password == null || password.length() < getMinPasswordLength()) {
            throw new WebServiceException("Password is too short!");
        }
    }

    /**
     * Verifies that the given username doesn't exist
     * 
     * @param username
     *            The username to check
     * @throws UsernameExistsException
     *             Thrown if username already exists
     */
    private static void verifyUsernameAvailability(String username) throws WebServiceException {
        if (!checkUsernameAvailability(username)) {
            throw new WebServiceException("User already exists!");
        }
    }

    /**
     * Makes sure the given username is long enough
     * 
     * @param username
     *            Username to check
     * @throws TooShortUsernameException
     *             Thrown if username is null or too short
     */
    private static void verifyUsernameLength(String username) throws WebServiceException {
        if (username == null || username.length() < getMinUsernameLength()) {
            throw new WebServiceException("User name is too short!");
        }
    }

    /**
     * This method tries to register a new user
     * 
     * @param username
     *            Desired username
     * @param password
     *            Desired password
     * @param verifyPassword
     *            Verification of the desired password
     * @return The created user instance
     */
    public static void verifyUserRestrictions(final User user) throws WebServiceException {
        verifyUsernameLength(user.getLogin());
        verifyPasswordLength(user.getPassword());
        checkPasswordVerification(user.getPassword(), user.getDuplicatePassword());
        verifyUsernameAvailability(user.getLogin());
    }

    public static User register(User user) {
        verifyUserRestrictions(user);
        encryptPassword(user);
        final User u = CriteriaQueryUtil.saveEntity(user);
        if (u == null) {
            throw new WebServiceException("Registration failed");
        }
        return u;
    }

}
